var sys = require('sys'),
    net = require('net'),
    util = require('../tools/util'),
    Handler = require('../handlers/connection').Connection,
    Amqp = require('../amqp').Amqp;
/**
 * Work with socket connections.
 *
 * The connection class provides methods for a client to establish a network connection to a
 * server, and for both peers to operate the connection thereafter.
 *
 * @class
 * @extends events.EventEmitter
 * @exports Connection as impl.Connection
 * @param {Object} cfg
 * @param {Object} cfg.clientProperties This string provides a set of peer properties, used for
 *      identification, debugging, and general information
 * @param {String} cfg.login
 * @param {String} cfg.password
 * @param {String} cfg.mechanism A single security mechanisms selected by the client, which must be
 *      one of those specified by the server.
 * @param {String} cfg.locale A single message local selected by the client, which must be one of
 *      those specified by the server.
 */
function Connection(/**Object*/cfg){
    var heartbeatTimer, connection  = this, handler = this.handler = new Handler();
    function startHeartbeat(/**Number*/heartbeat){
        stopHeartbeat();
        heartbeatTimer = setInterval(function(){
            handler.processFrame({ type: Amqp.Const.FrameHeartbeat });
        }, heartbeat * 1000);
    }
    function stopHeartbeat(){
        if (heartbeatTimer) {
            clearInterval(heartbeatTimer);
        }
    }
    /**
     * Start connection negotiation.
     * <br />
     * This method starts the connection negotiation process by telling the client the protocol
     * version that the server proposes, along with a list of security mechanisms which the client
     * can use for authentication.
     *
     * @name Connection#start
     * @event
     * @param {Object} args
     * @param {Number} versionMajor The protocol major version that the server agrees to use, which
     *      cannot be higher than the client's major version.
     * @param {Number} versionMinor The protocol minor version that the server agrees to use, which
     *      cannot be higher than the client's minor version.
     * @param {Object} serverProperties This string provides a set of peer properties, used for
     *      identification, debugging, and general information.
     * @param {String} mechanisms A list of the security mechanisms that the server supports,
     *      delimited by spaces. Currently ASL supports these mechanisms: PLAIN.
     * @param {String} locales A list of the message locales that the server supports, delimited by
     *      spaces. The locale defines the language in which the server will send reply texts.
     */
    handler.addListener('Amqp.Connection.Start', function(frame){
        if (-1 == util.Array.indexOf(frame.method.arguments.locales, cfg.locale)) {
            throw new Error('Locale <' + cfg.locale + '> is not supported. Supported locales: [' +
                    frame.method.arguments.locales.join(', ') + ']');
        }
        if (-1 == util.Array.indexOf(frame.method.arguments.mechanisms, cfg.mechanism)) {
            throw new Error('Auth mechanism <' + cfg.mechanism + '> is not supported. Supported ' +
                    'mechanisms: [' + frame.method.arguments.mechanisms.join(', ') + ']');
        }
        this.call(Amqp.Class.Connection, Amqp.Class.Connection.StartOk,
                { clientProperties: cfg.clientProperties, mechanism: cfg.mechanism, response:
                { LOGIN: cfg.login, PASSWORD: cfg.password }, locale: cfg.locale });
        connection.emit('start', frame.method.arguments);
    });
    /**
     * Propose connection tuning parameters.
     * <br />
     * This method proposes a set of connection configuration values to the client. The client can
     * accept and/or adjust these.
     *
     * @name Connection#tune
     * @event
     * @param {Object} args
     * @param {Number} channelMax The maximum total number of channels that the server allows per
     *      connection. Zero means that the server does not impose a fixed limit, but the number of
     *      allowed channels may be limited by available server resources.
     * @param {Number} frameMax he largest frame size that the server proposes for the connection.
     *      The client can negotiate a lower value. Zero means that the server does not impose any
     *      specific limit but may reject very large frames if it cannot allocate resources for
     *      them.
     * @param {Number} heartbeat The delay, in seconds, of the connection heartbeat that the server
     *      wants. Zero means the server does not want a heartbeat.
     */
    handler.addListener('Amqp.Connection.Tune', function(frame){
        if (frame.method.arguments.heartbeat) {
            startHeartbeat(frame.method.arguments.heartbeat);
        }
        this.frameMax = frame.method.arguments.frameMax;
        this.channelMax = frame.method.arguments.channelMax;
        if (!this.channelMax) {
            this.channelMax = cfg.channelMax;
        }
        this.call(Amqp.Class.Connection, Amqp.Class.Connection.TuneOk, {
            channelMax: frame.method.arguments.channelMax,
            heartbeat: frame.method.arguments.heartbeat });
        connection.emit('tune', frame.method.arguments);
    });
    /**
     * Asks the client to use a different server.
     *
     * This method redirects the client to another server, based on the requested virtual host
     * and/or capabilities.
     *
     * @name Connection#redirect
     * @event
     * @param {Object} args
     * @param {String} args.host Specifies the server to connect to. This is an IP address or a DNS
     *      name, optionally followed by a colon and a port number. If no port number is specified,
     *      the client should use the default port number for the protocol.
     * @param {Array} args.knownHosts Specifies the list of equivalent or alternative hosts that
     *      the server knows about, which will normally include the current server itself. Clients
     *      can cache this information and use it when reconnecting to a server after a failure.
     */
    handler.addListener('Amqp.Connection.Redirect', function(frame){
        connection.emit('redirect', frame.method.arguments);
    });
    /**
     * Signal that the connection is ready.
     *
     * This method signals to the client that the connection is ready for use.
     *
     * @name Connection#open
     * @event
     * @param {Object} args
     * @param {Array} args.knownHosts Specifies the list of equivalent or alternative hosts that
     *      the server knows about, which will normally include the current server itself. Clients
     *      can cache this information and use it when reconnecting to a server after a failure.
     */
    handler.addListener('Amqp.Connection.OpenOk', function(frame){
        connection.emit('open', frame.method.arguments);
    });
    /**
     * Inform that connection is closed.
     *
     * This method indicates that the sender or client wants to close the connection. This may be
     * due to internal conditions (e.g. a forced shut-down) or due to an error handling a specific
     * method, i.e. an exception. When a close is due to an exception, the sender provides the
     * class and method id of the method which caused the exception.
     *
     * @name Connection#close
     * @event
     * @param {Number} replyCode The reply code. The AMQ reply codes are defined in AMQ RFC 011.
     * @param {String} replyText The localised reply text. This text can be logged as an aid to
     *      resolving issues.
     * @param {Number} classId
     * @param {Number} methodId
     */
    handler.addListener('Amqp.Connection.Close', function(frame){
        this.call(Amqp.Class.Connection, Amqp.Class.Connection.CloseOk);
        connection.emit('close', frame.method.arguments);
    });
    handler.addListener('Amqp.Connection.CloseOk', function(frame){
        connection.emit('close', frame.method.arguments);
    });
    this.addListener('close', function(){
        this.handler.close();
        stopHeartbeat();
    });
}
sys.inherits(Connection, require('events').EventEmitter);
/**
 * Connect to Amqp server.
 *
 * @param port
 * @param host
 */
Connection.prototype.connect = function(/**Number*/port, /**String*/host){
    this.handler.connect(port, host);
};
/**
 * Open connection to virtual host.
 *
 * This method opens a connection to a virtual host, which is a collection of resources, and acts
 * to separate multiple application domains within a server.
 *
 * @param {Object} args
 * @param {String} args.virtualHost Must start with a slash "/" and continue with path names
 *      separated by slashes. A path name consists of any combination of at least one of
 *      [A-Za-z0-9] plus zero or more of [.-_+!=:].
 * @param {String} args.capabilities The client may specify a number of capability names, delimited
 *      by spaces. The server can use this string to how to process the client's connection
 *      request.
 * @param {Boolean} args.insist In a configuration with multiple load-sharing servers, the server
 *      may respond to a Connection.Open method with a Connection.Redirect. The insist option tells
 *      the server that the client is insisting on a connection to the specified server.
 * @return
 */
Connection.prototype.open = function(args){
    this.handler.call(Amqp.Class.Connection, Amqp.Class.Connection.Open, args);
};
/**
 * Request a connection close.
 *
 * This method indicates that the sender wants to close the connection. This may be due to internal
 * conditions (e.g. a forced shut-down) or due to an error handling a specific method, i.e. an
 * exception. When a close is due to an exception, the sender provides the class and method id of
 * the method which caused the exception.
 *
 * @param {Object} args
 * @param {Number} args.replyCode The reply code. The AMQ reply codes are defined in AMQ RFC 011.
 * @param {String} replyText The localised reply text. This text can be logged as an aid to
 *      resolving issues.
 * @param {Number} args.classId
 * @param {Number} args.methodId
 */
Connection.prototype.close = function(args){
    this.handler.call(Amqp.Class.Connection, Amqp.Class.Connection.Close, args);
};
exports.Connection = Connection;
